#pragma once

#include <iostream>
#include <memory>
#include <sys/epoll.h>

#include "Log.hpp"
#include "Socket.hpp"
#include "InetAddr.hpp"

using namespace InetAddrMoudle;
using namespace LogMoudle;
using namespace SocketMoudle;

const int gredefault = -1;

#define MAX 4096

class EpollServer
{
    static const int _revs_number = 256;

private:
    int _port;
    std::unique_ptr<Socket> _listen_socket;
    bool _isruning;
    int _epfd;
    struct epoll_event _revs[_revs_number];

public:
    EpollServer(int port) : _port(port), _isruning(false), _epfd(gradefault),
                            _listen_socket(std::make_unique<TcpSocket>())
    {
    }

    void Init()
    {
        _listen_socket->BuildTcpSocket(_port);
        // 创建epoll模型

        _epfd = epoll_create1(256);
        if (_epfd < 0)
        {
            LOG(Logevel::ERROR) << "epoll create error";
            exit(1);
        }

        LOG(Logevel::INFO) << "epoll create success: " << _epfd;

        // 将套接字添加到epoll模型中

        struct epoll_event ev;

        ev.events = EPOLLIN;
        ev.data.fd = _listen_socket->Fd();

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_socket->Fd(), &ev);

        if (n < 0)
        {
            LOG(Logevel::ERROR) << "epoll_ctl is error";
            exit(1);
        }

        LOG(Logevel::INFO) << "epoll_ctl success";
    }

    void Loop()
    {

        int timeout = -1;
        _isruning = true;

        while (_isruning)
        {
            int n = epoll_wait(_epfd, _revs, _revs_number, timeout);
            switch (n)
            {
            case -1:
                perror("wait error");
                exit(1);
                break;
            case 0:
                LOG(Logevel::INFO) << "time out......";
                break;
            default:
                LOG(Logevel::INFO) << "有事件就绪.....";
                Dispatcher(n);
                break;
            }
        }
        _isruning = false;
    }

    void Accepter()
    {
        InetAddr cline;

        int new_fd = _listen_socket->Accepter(&cline);
        if (new_fd < 0)
        {
            LOG(Logevel::ERROR) << "accepter error";
            return;
        }
        else
        {
            LOG(Logevel::INFO) << "获取一个新链接： " << new_fd << " cline info: " << cline.PrintDebug();

            // 将新加入的的new_fd添加到epoll中进行监管

            // epoll内部的红黑树结构，会自动管理，避免重复

            struct epoll_event ev;
            ev.events = EPOLLIN;
            ev.data.fd = new_fd;

            int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, new_fd, &ev);

            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                close(newfd);
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << newfd;
        }
    }

    // 回到函数，内部缝合了我们的任务：接收并发送数据
    void Recver(int fd)
    {
        // 接收就绪/合法、普通的fd
        char buffer[1024];

        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);

        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "cline# " << buffer << std::endl;

            std::string message = "echo# ";
            message += buffer;

            send(fd, message.c_str(), message.size(), 0);
        }
        else if (n == 0)
        {
            LOG(Logevel::INFO) << "客户端退出,sockfd: " << fd;

            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);

            if (m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                return;
            }

            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
            close(fd);
        }
        else
        {
            LOG(LogLevel::ERROR) << "客户端读取出错, sockfd: " << fd;

            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);

            if (m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                return;
            }
            LOG(LogLevel::DEBUG) << "epoll_ctl success: " << fd;
            close(fd);
        }
    }

    void Dispatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int events = _revs[i].events;
            int fd = _revs[i].data.fd;
            if (fd == _listen_socket->Fd())
            {
                if (events & EPOLLIN)
                {
                    Accepter();
                }
            }
            else
            {
                if (events & EPOLLIN)
                {
                    // 读事件就绪
                    Recver(fd);
                }
            }
        }
    }

    ~EpollServer()
    {
        _listen_socket->Close();
        if (_epfd >= 0)
        {
            close(_epfd);
        }
    }
};